#ifndef MOLECULE_HEADER_VIZMF_PROJECT
#define MOLECULE_HEADER_VIZMF_PROJECT

#include <vector>
#include <list>
#include <string>

#include "src/core/workspace/molecule/atom.h"
#include "src/core/workspace/molecule/bond.h"

/*
  VizMF Molecule class
*/

class Molecule
{
private:
  typedef std::vector<Atom> VA;  // must have random access iterators

public:
  typedef Atom atom_type;
  typedef VA::iterator atom_iterator;
  typedef VA::const_iterator atom_const_iterator;
  typedef Bond<atom_const_iterator> bond_type;	// bond is formed by a pair of iterators to atoms
  typedef unsigned int int_type;
  typedef float float_type;
  typedef std::string string;

private:
  typedef std::vector<bond_type> VB;

public:
  typedef VB::iterator bond_iterator;
  typedef VB::const_iterator bond_const_iterator; // not sure if it is really const

public:
  Molecule(int_type number = 0, const string name = string())
    : number_(number), name_(name) {}
  Molecule(const Molecule& other) : number_(other.number_), name_(other.name_), atoms_(other.atoms_)
  {
    // proper copy algorithm for bonds
    atom_const_iterator atomsBeg = atoms_.begin(), atomsOldBeg = other.atoms_.begin();
    bonds_.reserve(other.bonds_.size());
    for (bond_const_iterator bondOld = other.bonds_.begin(), bondOldE = other.bonds_.end(); bondOld!=bondOldE; ++bondOld)
    {
      bonds_.push_back(bond_type(bondOld->number(), atomsBeg + ( bondOld->from() - atomsOldBeg ), atomsBeg + ( bondOld->to() - atomsOldBeg ), bondOld->order()));
    }
  }

  int_type number() const { return number_; }
  void setNumber(int_type num) { number_ = num; }

  const string& name() const { return name_; }
  void setName (const string& name) { name_ = name; }

  // let STL do the hard work, we just reap results :)
  atom_iterator atomsBegin()
  {
    return atom_iterator(atoms_.begin());
  }
  atom_iterator atomsEnd()
  {
    return atom_iterator(atoms_.end());
  }

  atom_const_iterator atomsBegin() const
  {
    return atom_const_iterator(atoms_.begin());
  }
  atom_const_iterator atomsEnd() const
  {
    return atom_const_iterator(atoms_.end());
  }

  bond_iterator bondsBegin()
  {
    return bond_iterator(bonds_.begin());
  }
  bond_iterator bondsEnd()
  {
    return bond_iterator(bonds_.end());
  }

  bond_const_iterator bondsBegin() const
  {
    return bond_const_iterator(bonds_.begin());
  }
  bond_const_iterator bondsEnd() const
  {
    return bond_const_iterator(bonds_.end());
  }

  //next two functions are provided for selection mechanism
  const Atom atomAt(int index) const { return atoms_.at(index); }
  const bond_type bondAt(int index) const { return bonds_.at(index); }

  // Atom manipulations
  void addAtom(const atom_type& atom) { atoms_.push_back(atom); }
  atom_iterator removeAtom(atom_iterator atom_to_remove)
  {
    // deleting unnesesary bonds
    for (bond_iterator i = bondsBegin(), e = bondsEnd(); i!= e; ++i)
    {
      if (i->connects(bond_type::atom_handler(atom_to_remove)) )
      {
        i = bonds_.erase(i);
        i--;
      }
    }
    atoms_.erase(atom_to_remove);
    return ++atom_to_remove;
  }
  atom_iterator removeAtoms(atom_iterator begin_atom, atom_iterator end_atom) {
    for (atom_iterator i = begin_atom; i != end_atom; ++i)
    {
      for (bond_iterator j = bondsBegin(), e = bondsEnd(); j!= e; ++j)
      {
        if (j->connects(bond_type::atom_handler(i)) )
        {
          j = bonds_.erase(j);
          j--;
        }
      }
    }
    atoms_.erase(begin_atom, end_atom);
    return end_atom;
  }

  // Bond manipulations
  void addBond(const bond_type& bond) { bonds_.push_back(bond); }
  bond_iterator removeBond(bond_iterator bond_to_remove)
  {
    return bonds_.erase(bond_to_remove);
  }
  bond_iterator removeBonds(bond_iterator begin_bond, bond_iterator end_bond)
  {
    return bonds_.erase(begin_bond, end_bond);
  }

#ifdef OPTIONAL_FEATURES
  float_type charge() const { return charge_; };
  void setCharge(float_type charge) { charge_ = charge; };

  int_type multiplicity() const { return multiplicity_; };
  void setMultiplicity(int_type mult) { multiplicity_ = mult; };
#endif //OPTIONAL_FEATURES

private:
  int_type number_;
  string name_;	// description
  VA atoms_;
  VB bonds_;

#ifdef OPTIONAL_FEATURES
  float_type charge_;	// total molecule charge
  int_type multiplicity_;	// total molecule multiplicity
#endif //OPTIONAL_FEATURES

};

#endif // MOLECULE_HEADER_VIZMF_PROJECT
